﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using DotNetCommon.Extensions;

namespace DotNetCommon.Validate;


/// <summary>
/// 字符串校验扩展
/// </summary>
public static class ValidateStringExtensions
{
    #region MustBeString
    /// <summary>
    /// 则字符串必须为某个特定值
    /// </summary>
    public static IValidateContext<string> MustBeString(this IValidateContext<string> ctx, string val, bool ignoreCase = false, string errorMessage = null)
    {
        MustBeStringInternal(ctx, val, ignoreCase, errorMessage);
        return ctx;
    }

    internal static void MustBeStringInternal(IValidateContext ctx, string val, bool ignoreCase, string errorMessage)
    {
        if (ctx.StopValidate) return;
        if (!string.Equals(ctx.ModelValue?.ToString(), val, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 必须为 {val?.ToString() ?? "null"} !");
        }
    }

    /// <summary>
    /// 字符串不能某个特定值
    /// </summary>
    public static IValidateContext<string> MustNotBeString(this IValidateContext<string> ctx, string val, bool ignoreCase = false, string errorMessage = null)
    {
        MustNotBeStringInternal(ctx, val, ignoreCase, errorMessage);
        return ctx;
    }

    internal static void MustNotBeStringInternal(IValidateContext ctx, string val, bool ignoreCase, string errorMessage)
    {
        if (ctx.StopValidate) return;
        if (string.Equals(ctx.ModelValue?.ToString(), val, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不能是 {val?.ToString() ?? "null"} !");
        }
    }
    #endregion

    #region MustIn
    /// <summary>
    /// 字符串必须是其中之一
    /// </summary>
    public static IValidateContext<string> MustInString(this IValidateContext<string> ctx, IEnumerable<string> vals, bool ignoreCase = false, string errorMessage = null)
    {
        MustInStringInternal(ctx, vals, ignoreCase, errorMessage);
        return ctx;
    }
    internal static void MustInStringInternal(IValidateContext ctx, IEnumerable<string> vals, bool ignoreCase = false, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        //这里不能放过 list 为null的情况, 如果真的是想故意验证失败, 可以传 [] 而不是null
        AssertUtil.NotNull(vals);
        if (!vals.Contains(ctx.ModelValue?.ToString(), ignoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal))
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 必须为 {vals.ToJson()} 之一!");
        }
    }
    /// <summary>
    /// 字符串不能是其中之一
    /// </summary>
    public static IValidateContext<string> MustNotInString(this IValidateContext<string> ctx, IEnumerable<string> vals, bool ignoreCase = false, string errorMessage = null)
    {
        MustNotInStringInternal(ctx, vals, ignoreCase, errorMessage);
        return ctx;
    }
    internal static void MustNotInStringInternal(IValidateContext ctx, IEnumerable<string> vals, bool ignoreCase = false, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (vals.IsNullOrEmpty()) return;
        if (vals.Contains(ctx.ModelValue?.ToString(), ignoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal))
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不能是 {vals.ToJson()} 之一!");
        }
    }
    #endregion

    #region string.Null NullOrEmpty NullOrEmptyOrWhiteSpace
    /// <summary>
    /// 字符串必须为空字符串
    /// </summary>
    public static IValidateContext<string> MustBeNullOrEmpty(this IValidateContext<string> ctx, string errorMessage = null)
    {
        MustBeNullOrEmptyInternal(ctx, errorMessage);
        return ctx;
    }

    internal static void MustBeNullOrEmptyInternal(IValidateContext ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if ((ctx.ModelValue?.ToString() ?? null).IsNotNullOrEmpty() == true)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 必须为空!");
        }
    }

    /// <summary>
    /// 字符串不能为空
    /// </summary>
    public static IValidateContext<string> MustNotNullOrEmpty(this IValidateContext<string> ctx, string errorMessage = null)
    {
        MustNotNullOrEmptyInternal(ctx, errorMessage);
        return ctx;
    }
    internal static void MustNotNullOrEmptyInternal(IValidateContext ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if ((ctx.ModelValue?.ToString() ?? null).IsNullOrEmpty() == true)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不能为空!");
        }
    }

    /// <summary>
    /// 字符串必须为null或空字符串或空白字符
    /// </summary>
    public static IValidateContext<string> MustBeNullOrWhiteSpace(this IValidateContext<string> ctx, string errorMessage = null)
    {
        MustBeNullOrWhiteSpaceInternal(ctx, errorMessage);
        return ctx;
    }

    internal static void MustBeNullOrWhiteSpaceInternal(IValidateContext ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if ((ctx.ModelValue?.ToString() ?? null).IsNotNullOrWhiteSpace() == true)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 必须为空!");
        }
    }

    /// <summary>
    /// 字符串不能为空
    /// </summary>
    public static IValidateContext<string> MustNotNullOrWhiteSpace(this IValidateContext<string> ctx, string errorMessage = null)
    {
        MustNotNullOrWhiteSpaceInternal(ctx, errorMessage);
        return ctx;
    }
    internal static void MustNotNullOrWhiteSpaceInternal(IValidateContext ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if ((ctx.ModelValue?.ToString() ?? null).IsNullOrWhiteSpace() == true)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不能为空!");
        }
    }
    #endregion

    #region MustLength
    /// <summary>
    /// 字符串长度范围
    /// </summary>
    public static IValidateContext<string> MustLengthInRange(this IValidateContext<string> ctx, int? minLength = null, int? maxLength = null, string errorMessage = null)
    {
        MustLengthInRangeInternal(ctx, ctx.Model, minLength, maxLength, errorMessage);
        return ctx;
    }

    internal static void MustLengthInRangeInternal(IValidateContext ctx, string str, int? minLength = null, int? maxLength = null, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (str == null)
        {
            if (minLength > 0)
            {
                ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 的长度必须在 {minLength}-{maxLength} 之间 !");
            }
        }
        else
        {
            if (minLength > 0 && str.Length < minLength)
            {
                ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 的长度必须在 {minLength}-{maxLength} 之间 !");
            }
            if (maxLength > 0 && str.Length > maxLength)
            {
                ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 的长度必须在 {minLength}-{maxLength} 之间 !");
            }
        }
    }

    /// <summary>
    /// 字符串长度最大是多少
    /// </summary>
    /// <remarks>注意: 当字符串为null时, 验证直接通过</remarks>
    public static IValidateContext<string> MustMaxLength(this IValidateContext<string> ctx, int len, string errorMessage = null)
    {
        MustMaxLengthInternal(ctx, ctx.Model, len, errorMessage);
        return ctx;
    }

    internal static void MustMaxLengthInternal(IValidateContext ctx, string str, int len, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (str == null) return;
        if (str.Length > len)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 的长度不能超过 {len} !");
        }
    }

    /// <summary>
    /// 字符串长度最小是多少
    /// </summary>
    public static IValidateContext<string> MustMinLength(this IValidateContext<string> ctx, int len, string errorMessage = null)
    {
        MustMinLengthInternal(ctx, ctx.Model, len, errorMessage);
        return ctx;
    }

    internal static void MustMinLengthInternal(IValidateContext ctx, string str, int len, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (len <= 0) return;
        if (str == null || str.Length < len)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 的长度不能小于 {len} !");
        }
    }
    #endregion

    #region MustContains
    /// <summary>
    /// 字符串中必须包含value
    /// </summary>
    /// <remarks>
    /// 特别的:
    /// <list type="bullet">
    /// <item>任意字符串都包含null</item>
    /// <item>任意非null字符串都包含""</item>
    /// </list>
    /// </remarks>
    public static IValidateContext<string> MustContains(this IValidateContext<string> ctx, string value, bool ignoreCase = false, string errorMessage = null)
    {
        MustContainsInternal(ctx, ctx.Model, value, ignoreCase, errorMessage);
        return ctx;
    }
    internal static void MustContainsInternal(IValidateContext ctx, string str, string value, bool ignoreCase = false, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (value == null) return;
        if (str == null)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 中必须包含 {value} !");
            return;
        }
        if (str.Contains(value, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) != true)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 中必须包含 {value} !");
        }
    }

    /// <summary>
    /// 字符串中必须包含提供的项之一
    /// </summary>
    /// <remarks>
    /// 特别的:
    /// <list type="bullet">
    /// <item>任意字符串都包含null</item>
    /// <item>任意非null字符串都包含""</item>
    /// </list>
    /// 另外, 提供的项集合必须有意义, 否则直接通过, 如: 空集合没有意义, 没必要执行验证
    /// </remarks>
    public static IValidateContext<string> MustContainsAtleastOne(this IValidateContext<string> ctx, IEnumerable<string> values, bool ignoreCase = false, string errorMessage = null)
    {
        MustContainsAtleastOneInternal(ctx, ctx.Model, values, ignoreCase, errorMessage);
        return ctx;
    }
    internal static void MustContainsAtleastOneInternal(IValidateContext ctx, string str, IEnumerable<string> values, bool ignoreCase = false, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (values.IsNullOrEmpty() || values.Any(i => i == null)) return;
        if (str == null)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 中必须包含 {values.ToJson()} 之一!");
            return;
        }
        if (values.Any(i => str.Contains(i, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))) return;
        ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 中必须包含 {values.ToJson()} 之一!");
    }

    /// <summary>
    /// 字符串中必须包含提供的所有项
    /// </summary>
    /// <remarks>
    /// 特别的:
    /// <list type="bullet">
    /// <item>任意字符串都包含null</item>
    /// <item>任意非null字符串都包含""</item>
    /// </list>
    /// 另外, 提供的项集合必须有意义, 否则直接通过, 如: 空集合没有意义, 没必要执行验证
    /// </remarks>
    public static IValidateContext<string> MustContainsAll(this IValidateContext<string> ctx, IEnumerable<string> values, bool ignoreCase = false, string errorMessage = null)
    {
        MustContainsAllInternal(ctx, ctx.Model, values, ignoreCase, errorMessage);
        return ctx;
    }
    internal static void MustContainsAllInternal(IValidateContext ctx, string str, IEnumerable<string> values, bool ignoreCase = false, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (values.IsNullOrEmpty() || values.All(i => i == null)) return;
        if (str == null)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 中必须包含 {values.ToJson()} 中的所有项!");
            return;
        }
        if (values.Any(i => i != null && !str.Contains(i, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)))
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 中必须包含 {values.ToJson()} 中的所有项!");
            return;
        }
    }

    /// <summary>
    /// 字符串不能包含value
    /// </summary>
    /// <remarks>
    /// 注意: 提供的禁用项必须是非null和非""的, 否则直接通过, 因为没有意义
    /// </remarks>
    public static IValidateContext<string> MustNotContains(this IValidateContext<string> ctx, string value, bool ignoreCase = false, string errorMessage = null)
    {
        MustNotContainsInternal(ctx, ctx.Model, value, ignoreCase, errorMessage);
        return ctx;
    }
    internal static void MustNotContainsInternal(IValidateContext ctx, string str, string value, bool ignoreCase = false, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (value.IsNullOrEmpty()) return;
        if (str.IsNullOrEmpty()) return;
        if (str.Contains(value, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 中不能包含 {value} !");
        }
    }

    /// <summary>
    /// 字符串不能包含提供的任意一项
    /// </summary>
    /// <remarks>
    /// 注意: 提供的禁用项必须是非null和非""的, 否则直接通过, 因为没有意义<br/>
    /// 另外, 提供的项集合必须有意义, 否则直接通过, 如: 空集合没有意义, 没必要执行验证
    /// </remarks>
    public static IValidateContext<string> MustNotContainsAny(this IValidateContext<string> ctx, IEnumerable<string> values, bool ignoreCase = false, string errorMessage = null)
    {
        MustNotContainsAnyInternal(ctx, ctx.Model, values, ignoreCase, errorMessage);
        return ctx;
    }
    internal static void MustNotContainsAnyInternal(IValidateContext ctx, string str, IEnumerable<string> values, bool ignoreCase = false, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (values.IsNullOrEmpty()) return;
        if (str.IsNullOrEmpty()) return;
        if (values.Any(i => i.IsNotNullOrEmpty() && str.Contains(i, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)))
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 中不能包含 {values.ToJson()} 任意一项!");
        }
    }
    #endregion

    #region MustMatchRegex
    /// <summary>
    /// 字符串必须匹配正则表达式
    /// </summary>
    /// <remarks>
    /// 注意: 提供的正则必须是有意义的(如, 不能是null), 否则直接通过<br/>另外: 为null的字符串不能匹配任何正则
    /// </remarks>
    public static IValidateContext<string> MustMatchRegex(this IValidateContext<string> ctx, string pattern, string errorMessage = null)
    {
        MustMatchRegexInternal(ctx, ctx.Model, pattern, errorMessage);
        return ctx;
    }

    internal static void MustMatchRegexInternal(IValidateContext ctx, string str, string pattern, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (pattern.IsNullOrEmpty()) return;
        if (str == null)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不匹配给定的模式!");
            return;
        }
        var reg = new Regex(pattern);
        if (!reg.IsMatch(str))
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不匹配给定的模式!");
        }
        return;
    }

    /// <summary>
    /// 字符串至少匹配多个正则表达式之一
    /// </summary>
    /// <remarks>
    /// 注意: 提供的正则必须是有意义的(如, 不能是null), 否则直接通过<br/>另外: 为null的字符串不能匹配任何正则
    /// </remarks>
    public static IValidateContext<string> MustMatchRegexAtleastOne(this IValidateContext<string> ctx, IEnumerable<string> patterns, string errorMessage = null)
    {
        MustMatchRegexAtleastOneInternal(ctx, ctx.Model, patterns, errorMessage);
        return ctx;
    }

    internal static void MustMatchRegexAtleastOneInternal(IValidateContext ctx, string str, IEnumerable<string> patterns, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (patterns.IsNullOrEmpty()) return;
        if (patterns.All(i => i.IsNullOrEmpty())) return;
        if (str == null)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不匹配给定的任意模式!");
            return;
        }
        foreach (var pattern in patterns)
        {
            if (new Regex(pattern).IsMatch(str)) return;
        }
        ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不匹配给定的任意模式!");
        return;
    }

    /// <summary>
    /// 字符串必须全部匹配给定的正则表达式
    /// </summary>
    /// <remarks>
    /// 注意: 提供的正则必须是有意义的(如, 不能是null), 否则直接通过<br/>另外: 为null的字符串不能匹配任何正则
    /// </remarks>
    public static IValidateContext<string> MustMatchRegexAll(this IValidateContext<string> ctx, IEnumerable<string> patterns, string errorMessage = null)
    {
        MustMatchRegexAllInternal(ctx, ctx.Model, patterns, errorMessage);
        return ctx;
    }

    internal static void MustMatchRegexAllInternal(IValidateContext ctx, string str, IEnumerable<string> patterns, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (patterns.IsNullOrEmpty()) return;
        if (patterns.All(i => i.IsNullOrEmpty())) return;
        if (str == null)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不匹配给定的模式之一!");
            return;
        }
        foreach (var pattern in patterns)
        {
            var reg = new Regex(pattern);
            if (!reg.IsMatch(str))
            {
                ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不匹配给定的模式之一!");
                return;
            }
        }
    }

    /// <summary>
    /// 字符串不能匹配给定的正则表达式
    /// </summary>
    public static IValidateContext<string> MustNotMatchRegex(this IValidateContext<string> ctx, string pattern, string errorMessage = null)
    {
        MustNotMatchRegexInternal(ctx, ctx.Model, pattern, errorMessage);
        return ctx;
    }
    internal static void MustNotMatchRegexInternal(IValidateContext ctx, string str, string pattern, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (pattern.IsNullOrEmpty()) return;
        if (str == null) return;
        var reg = new Regex(pattern);
        if (reg.IsMatch(str))
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不能匹配给定的模式!");
        }
    }

    /// <summary>
    /// 字符串不能匹配任意一个正则表达式
    /// </summary>
    public static IValidateContext<string> MustNotMatchRegexAny(this IValidateContext<string> ctx, IEnumerable<string> patterns, string errorMessage = null)
    {
        MustNotMatchRegexAnyInternal(ctx, ctx.Model, patterns, errorMessage);
        return ctx;
    }

    internal static void MustNotMatchRegexAnyInternal(IValidateContext ctx, string str, IEnumerable<string> patterns, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (patterns.IsNullOrEmpty()) return;
        if (patterns.All(i => i.IsNullOrEmpty())) return;
        if (str == null) return;
        foreach (var pattern in patterns)
        {
            var reg = new Regex(pattern);
            if (reg.IsMatch(str))
            {
                ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不能匹配给定的任意模式!");
                return;
            }
        }
    }
    #endregion

    #region MustEmailAddress
    /// <summary>
    /// 字符串必须是邮箱格式
    /// </summary>
    /// <remarks>
    /// 注意: 字符串不能为null, 如果期望在字符串不为null时再进行校验, 可参考下面:
    /// <code>
    /// var msg = VValid.ValidMessage(new Model(), ctx =>
    /// {
    ///     ctx.RuleFor(i => i.Email).When(i => i != null, ctx => ctx.MustEmailAddress());
    /// }
    /// </code>
    /// </remarks>
    public static IValidateContext<string> MustEmailAddress(this IValidateContext<string> ctx, string errorMessage = null)
    {
        MustEmailAddressInternal(ctx, ctx.Model, errorMessage);
        return ctx;
    }

    internal static void MustEmailAddressInternal(IValidateContext ctx, string str, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (str == null || !str.IsValidEmail())
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不是正确的邮箱!");
        }
    }
    #endregion

    #region MustIdCard
    /// <summary>
    /// 字符串必须是身份证格式
    /// </summary>
    /// <remarks>
    /// 注意: 字符串不能为null, 如果期望在字符串不为null时再进行校验, 可参考下面:
    /// <code>
    /// var msg = VValid.ValidMessage(new Model(), ctx =>
    /// {
    ///     ctx.RuleFor(i => i.IdCard).When(i => i != null, ctx => ctx.MustIdCard());
    /// }
    /// </code>
    /// </remarks>
    public static IValidateContext<string> MustIdCard(this IValidateContext<string> ctx, string errorMessage = null)
    {
        MustIdCardInternal(ctx, ctx.Model, errorMessage);
        return ctx;
    }
    internal static void MustIdCardInternal(IValidateContext ctx, string str, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (str == null || !str.IsChinaIdCard18())
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不是正确的身份证号!");
        }
    }
    #endregion

    #region MustCellPhone
    /// <summary>
    /// 字符串必须是手机号码(最宽松的: 11位, 以 '1' 开头即可, 另外: 兼容+区号, 如: "+8615012341234"、"+86 15012341234")
    /// </summary>
    /// <remarks>
    /// 注意: 字符串不能为null, 如果期望在字符串不为null时再进行校验, 可参考下面:
    /// <code>
    /// var msg = VValid.ValidMessage(new Model(), ctx =>
    /// {
    ///     ctx.RuleFor(i => i.PhoneNo).When(i => i != null, ctx => ctx.MustCellPhone());
    /// }
    /// </code>
    /// </remarks>
    public static IValidateContext<string> MustCellPhone(this IValidateContext<string> ctx, string errorMessage = null)
    {
        MustCellPhoneInternal(ctx, ctx.Model, errorMessage);
        return ctx;
    }

    internal static void MustCellPhoneInternal(IValidateContext ctx, string str, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (str == null || !str.IsPhoneNumber())
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不是正确的手机号码!");
        }
    }
    #endregion

    #region MustStartsEndsWith
    /// <summary>
    /// 字符串必须以给定值开头
    /// </summary>
    public static IValidateContext<string> MustStartsWith(this IValidateContext<string> ctx, string prefix, bool ignoreCase = false, string errorMessage = null)
    {
        MustStartsWithInternal(ctx, ctx.Model, prefix, ignoreCase, errorMessage);
        return ctx;
    }
    internal static void MustStartsWithInternal(IValidateContext ctx, string str, string prefix, bool ignoreCase = false, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (prefix.IsNullOrEmpty()) return;
        if (str == null)
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 必须是以 {prefix} 开头,而不是 null !");
        else if (!str.StartsWith(prefix, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 必须是以 {prefix} 开头!");
    }

    /// <summary>
    /// 字符串不能以给定值开头
    /// </summary>
    public static IValidateContext<string> MustNotStartsWith(this IValidateContext<string> ctx, string prefix, bool ignoreCase = false, string errorMessage = null)
    {
        MustNotStartsWithInternal(ctx, ctx.Model, prefix, ignoreCase, errorMessage);
        return ctx;
    }
    internal static void MustNotStartsWithInternal(IValidateContext ctx, string str, string prefix, bool ignoreCase = false, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (prefix.IsNullOrEmpty()) return;
        if ((str ?? "").StartsWith(prefix, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不能以 {prefix} 开头!");
    }

    /// <summary>
    /// 字符串必须以给定值结尾
    /// </summary>
    public static IValidateContext<string> MustEndsWith(this IValidateContext<string> ctx, string suffix, bool ignoreCase = false, string errorMessage = null)
    {
        MustEndsWithInternal(ctx, ctx.Model, suffix, ignoreCase, errorMessage);
        return ctx;
    }
    internal static void MustEndsWithInternal(IValidateContext ctx, string str, string suffix, bool ignoreCase = false, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (suffix.IsNullOrEmpty()) return;
        if (str == null)
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 必须是以 {suffix} 结尾,而不是 null !");
        else if (!str.EndsWith(suffix, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 必须是以 {suffix} 结尾!");
    }

    /// <summary>
    /// 字符串不能以给定值结尾
    /// </summary>
    public static IValidateContext<string> MustNotEndsWith(this IValidateContext<string> ctx, string suffix, bool ignoreCase = false, string errorMessage = null)
    {
        MustNotEndsWithInternal(ctx, ctx.Model, suffix, ignoreCase, errorMessage);
        return ctx;
    }
    internal static void MustNotEndsWithInternal(IValidateContext ctx, string str, string suffix, bool ignoreCase = false, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (suffix.IsNullOrEmpty()) return;
        if (str != null && str.EndsWith(suffix, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不能以 {suffix} 结尾!");
    }
    #endregion

    #region MustBeGuid
    /// <summary>
    /// 字符串必须是Guid格式
    /// </summary>
    /// <remarks>
    /// 注意: <c>format</c> 参数用以限制GUID的格式(空值表示不限定格式), 比如:
    /// <list type="bullet">
    /// <item>"N": "cd5abdcde9ce44d6bca48f5a8fda2e95"</item>
    /// <item>"D": "cd5abdcd-e9ce-44d6-bca4-8f5a8fda2e95"</item>
    /// <item>"B": "{cd5abdcd-e9ce-44d6-bca4-8f5a8fda2e95}"</item>
    /// <item>"P": "(cd5abdcd-e9ce-44d6-bca4-8f5a8fda2e95)"</item>
    /// <item>"X": "{0xcd5abdcd,0xe9ce,0x44d6,{0xbc,0xa4,0x8f,0x5a,0x8f,0xda,0x2e,0x95}}"</item>
    /// </list>
    /// 另外, 字符串不能为null, 如果期望在字符串不为null时再进行校验, 可参考下面:
    /// <code>
    /// var msg = VValid.ValidMessage(new Model(), ctx =>
    /// {
    ///     ctx.RuleFor(i => i.UUID).When(i => i != null, ctx => ctx.MustBeGuid());
    /// }
    /// </code>
    /// </remarks>
    public static IValidateContext<string> MustBeGuid(this IValidateContext<string> ctx, string format = null, bool ignoreCase = false, string errorMessage = null)
    {
        MustBeGuidInternal(ctx, ctx.Model, format, ignoreCase, errorMessage);
        return ctx;
    }

    internal static void MustBeGuidInternal(IValidateContext ctx, string str, string format = null, bool ignoreCase = false, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (str == null)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不能为空且格式必须符合一个GUID!");
            return;
        }
        if (!Guid.TryParse(str, out var guid))
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 必须是GUID格式的字符串!");
            return;
        }
        if (format != null)
        {
            if (!string.Equals(guid.ToString(format), str, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
            {
                var p = Guid.Parse("0".Repeat(32)).ToString(format);
                ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 必须是符合特定格式({format})的GUID字符串, 参考格式: '{p}'{(ignoreCase ? "" : "(注意: 必须是小写英文)")}!");
                return;
            }
        }
    }
    #endregion

    #region MustBeEnum
    /// <summary>
    /// 字符串必须是枚举字符串, 不同于 <seealso cref="MustCanConvertTo"/>, 这个方法可以严格限定字符串格式, 如:
    /// <code>
    /// enun EnumStatus { Open, Close }
    /// var error = VValid.ValidErrorMessage("close", ctx =>
    /// {
    ///     // 严格模式
    ///     // Enum.Parse(原始字符串).ToString() == 原始字符串
    ///     ctx.MustBeEnum&lt;EnumStatus?>(isStrict: true);//注意: 可空枚举对应null字符串
    /// });
    /// //out: '$' 必须是枚举字符串(DotNetCommon.Test.EnumStatus?)!
    /// </code>
    /// </summary>
    public static IValidateContext<string> MustBeEnum<T>(this IValidateContext<string> ctx, bool isStrict = false, string errorMessage = null)
    {
        MustBeEnumInternal<T>(ctx, ctx.Model, isStrict, errorMessage);
        return ctx;
    }

    internal static void MustBeEnumInternal<T>(IValidateContext ctx, string str, bool isStrict = false, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (str == null)
        {
            if (!typeof(T).IsNullable()) ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 不能为空!");
            return;
        }
        var type = typeof(T).GetNullAbleType();
        if (!Enum.TryParse(type, str, !isStrict, out var enumInst))
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 必须{(isStrict ? "严格" : "")}是枚举字符串({type.GetClassFullName()})!");
            return;
        }
        if (isStrict && enumInst.ToString() != str)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"'{ctx.ModelPath}' 必须严格是枚举字符串({type.GetClassFullName()})!");
            return;
        }
    }
    #endregion
}
